package sample.tree;

import java.util.*;
import sample.tree.node.expr.Expr;
import sample.tree.node.expr.Add;
import sample.tree.node.expr.Sub;
import sample.tree.node.expr.Mul;
import sample.tree.node.expr.Div;
import sample.tree.node.expr.Const;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.Map;
import java.util.HashMap;

public final class Context {

	/**
	 * After the call of this method, this Context has only all nodes reachable from argument node.
	 * @param root Root node of tree corresponding to this Context.
	 */
	public void purgeUnusedNodes(
			final Expr root)
	{
		final NodesCollector collector = new NodesCollector();
		root.visit(collector);
		assign(collector.context);
	}

	private void assign(final Context rhs) {

		mapAdd = rhs.mapAdd;
		mapSub = rhs.mapSub;
		mapMul = rhs.mapMul;
		mapDiv = rhs.mapDiv;
		mapConst = rhs.mapConst;
	}

	/**
	 * Factory method of 'Add' node.
	 * @return Instance of 'Add' class.
	 */
	public Expr getAdd(
			final Expr lhs,
			final Expr rhs)
	{
		return Add.get(
				this,
				lhs,
				rhs);
	}

	/**
	 * Factory method of 'Sub' node.
	 * @return Instance of 'Sub' class.
	 */
	public Expr getSub(
			final Expr lhs,
			final Expr rhs)
	{
		return Sub.get(
				this,
				lhs,
				rhs);
	}

	/**
	 * Factory method of 'Mul' node.
	 * @return Instance of 'Mul' class.
	 */
	public Expr getMul(
			final Expr lhs,
			final Expr rhs)
	{
		return Mul.get(
				this,
				lhs,
				rhs);
	}

	/**
	 * Factory method of 'Div' node.
	 * @return Instance of 'Div' class.
	 */
	public Expr getDiv(
			final Expr lhs,
			final Expr rhs)
	{
		return Div.get(
				this,
				lhs,
				rhs);
	}

	/**
	 * Factory method of 'Const' node.
	 * @return Instance of 'Const' class.
	 */
	public Expr getConst(
			final int value)
	{
		return Const.get(
				this,
				value);
	}

	private Map<Add, Add> mapAdd =
		new HashMap<Add, Add>();
	/**
	 * Ignore this method. (Used by generated classes only.)
	 */
	public Add intern(
			final Add node)
	{
		if(mapAdd.containsKey(node))
			return mapAdd.get(node);
		mapAdd.put(node, node);
		return node;
	}

	private Map<Sub, Sub> mapSub =
		new HashMap<Sub, Sub>();
	/**
	 * Ignore this method. (Used by generated classes only.)
	 */
	public Sub intern(
			final Sub node)
	{
		if(mapSub.containsKey(node))
			return mapSub.get(node);
		mapSub.put(node, node);
		return node;
	}

	private Map<Mul, Mul> mapMul =
		new HashMap<Mul, Mul>();
	/**
	 * Ignore this method. (Used by generated classes only.)
	 */
	public Mul intern(
			final Mul node)
	{
		if(mapMul.containsKey(node))
			return mapMul.get(node);
		mapMul.put(node, node);
		return node;
	}

	private Map<Div, Div> mapDiv =
		new HashMap<Div, Div>();
	/**
	 * Ignore this method. (Used by generated classes only.)
	 */
	public Div intern(
			final Div node)
	{
		if(mapDiv.containsKey(node))
			return mapDiv.get(node);
		mapDiv.put(node, node);
		return node;
	}

	private Map<Const, Const> mapConst =
		new HashMap<Const, Const>();
	/**
	 * Ignore this method. (Used by generated classes only.)
	 */
	public Const intern(
			final Const node)
	{
		if(mapConst.containsKey(node))
			return mapConst.get(node);
		mapConst.put(node, node);
		return node;
	}
}
